package pm.oo.util;

import com.sun.star.beans.NamedValue;
import com.sun.star.beans.PropertyValue;
import com.sun.star.container.XContainerQuery;
import com.sun.star.container.XEnumeration;
import com.sun.star.document.MacroExecMode;
import com.sun.star.document.UpdateDocMode;
import com.sun.star.document.XTypeDetection;
import com.sun.star.frame.*;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.task.XInteractionHandler;
import com.sun.star.task.XInteractionRequest;
import com.sun.star.task.XStatusIndicator;
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.XModifiable;
import pm.oo.util.OfficeConnection;

/**
 * @author Anton Bernatski
 * @version $Revision$ $Date$
 */
public class DocumentUtil {
	/**
	 * Load document specified by an URL into given frame synchronously.
	 * The result of this operation will be the loaded document for success
	 * or null if loading failed.
	 *
	 * @param xFrame	  frame wich should be the target of this openDocument call
	 * @param sURL		unparsed URL for loading
	 * @param lProperties optional arguments
	 * @return [XComponent]	the loaded document for success or null if it's failed
	 */
	public static XComponent loadDocument(XFrame xFrame, String sURL, PropertyValue[] lProperties) {
		XComponent xDocument = null;
		String sOldName = null;

		try {
			XComponentContext xCtx = OfficeConnection.getOfficeContext();

			// First prepare frame for loading
			// We must adress it inside the frame tree without any complications.
			// So we set an unambigous (we hope it) name and use it later.
			// Don't forget to reset original name after that.
			sOldName = xFrame.getName();
			String sTarget = "odk_officedev_desk";
			xFrame.setName(sTarget);

			// Get access to the global component loader of the office
			// for synchronous loading the document.
			Object desktopObj = xCtx.getServiceManager().createInstanceWithContext(
					"com.sun.star.frame.Desktop", xCtx);
			XComponentLoader xLoader =
					(XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, desktopObj);

			// Load the document into the target frame by using his name and
			// special search flags.
			xDocument = xLoader.loadComponentFromURL(
					sURL,
					sTarget,
					FrameSearchFlag.CHILDREN,
					lProperties);

			// dont forget to restore old frame name ...
			xFrame.setName(sOldName);
		}
		catch (com.sun.star.io.IOException exIO) {
			// Can be thrown by "loadComponentFromURL()" call.
			// The only thing we should do then is to reset changed frame name!
			exIO.printStackTrace();
			xDocument = null;
			if (sOldName != null) {
				xFrame.setName(sOldName);
			}
		}
		catch (com.sun.star.lang.IllegalArgumentException exIllegal) {
			// Can be thrown by "loadComponentFromURL()" call.
			// The only thing we should do then is to reset changed frame name!
			exIllegal.printStackTrace();
			xDocument = null;
			if (sOldName != null) {
				xFrame.setName(sOldName);
			}
		}
		catch (com.sun.star.uno.RuntimeException exRuntime) {
			// Any UNO method of this scope can throw this exception.
			// The only thing we can try(!) is to reset changed frame name.
			exRuntime.printStackTrace();
			xDocument = null;
			if (sOldName != null) {
				xFrame.setName(sOldName);
			}
		}
		catch (com.sun.star.uno.Exception exUno) {
			// "createInstance()" method of used service manager can throw it.
			// The only thing we should do then is to reset changed frame name!
			exUno.printStackTrace();
			xDocument = null;
			if (sOldName != null) {
				xFrame.setName(sOldName);
			}
		}

		return xDocument;
	}

	/**
	 * Try to close the document without any saving of modifications.
	 * We can try it only! Controller and/or model of this document
	 * can disagree with that. But mostly they doesn't do so.
	 *
	 * @param xDocument document which should be clcosed
	 */
	public static void closeDocument(XComponent xDocument) {
		try {
			// Check supported functionality of the document (model or controller).
			XModel xModel = (XModel) UnoRuntime.queryInterface(XModel.class, xDocument);

			if (xModel != null) {
				// It's a full featured office document.
				// Reset the modify state of it and closeCurrentDocument it.
				// Note: Model can disgree by throwing a veto exception.
				XModifiable xModify = (XModifiable) UnoRuntime.queryInterface(XModifiable.class, xModel);

				xModify.setModified(false);
				xDocument.dispose();
			} else {
				// It's a document which supports a controller .. or may by a pure
				// window only. If it's at least a controller - we can try to
				// suspend him. But - he can disagree with that!
				XController xController = (XController) UnoRuntime.queryInterface(XController.class, xDocument);

				if (xController != null) {
					if (xController.suspend(true) == true) {
						// Note: Don't dispose the controller - destroy the frame
						// to make it right!
						XFrame xFrame = xController.getFrame();
						xFrame.dispose();
					}
				}
			}
		}
		catch (com.sun.star.beans.PropertyVetoException exVeto) {
			// Can be thrown by "setModified()" call on model.
			// He disagree with our request.
			// But there is nothing to do then. Following "dispose()" call wasn't
			// never called (because we catch it before). Closing failed -that's it.
			exVeto.printStackTrace();
		}
		catch (com.sun.star.lang.DisposedException exDisposed) {
			// If an UNO object was already disposed before - he throw this special
			// runtime exception. Of course every UNO call must be look for that -
			// but it's a question of error handling.
			// For demonstration this exception is handled here.
			exDisposed.printStackTrace();
		}
		catch (com.sun.star.uno.RuntimeException exRuntime) {
			// Every uno call can throw that.
			// Do nothing - closing failed - that's it.
			exRuntime.printStackTrace();
		}
	}

	public static boolean check(String url) {
		XComponentContext xCtx = OfficeConnection.getOfficeContext();

		PropertyValue[][] testProperties = new PropertyValue[1][1];
		testProperties[0][0] = new PropertyValue();
		testProperties[0][0].Name = "URL";
		testProperties[0][0].Value = url;

		try {
			Object typeDetectionSrv = xCtx.getServiceManager().createInstanceWithContext("com.sun.star.document.TypeDetection", xCtx);
			XTypeDetection typeDetection = (XTypeDetection) UnoRuntime.queryInterface(XTypeDetection.class, typeDetectionSrv);

			Object filterFactorySrv = xCtx.getServiceManager().createInstanceWithContext("com.sun.star.document.FilterFactory", xCtx);
			XMultiServiceFactory filterFactory = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, filterFactorySrv);

			XContainerQuery xFilterContainerQuery = (XContainerQuery) UnoRuntime.queryInterface(XContainerQuery.class, filterFactory);

			NamedValue[] query = new NamedValue[1];
			query[0] = new NamedValue("Type", typeDetection.queryTypeByDescriptor(testProperties, true));
			XEnumeration xEnumeration = xFilterContainerQuery.createSubSetEnumerationByProperties(query);

			if (xEnumeration.hasMoreElements()) {
				Object object = xEnumeration.nextElement();
				PropertyValue[] propertyValue = (PropertyValue[]) AnyConverter.toArray(object);
				for (int i = 0; i < propertyValue.length; i++) {
					PropertyValue value = propertyValue[i];
					if ("DocumentService".equals(value.Name)) {
						return "com.sun.star.drawing.DrawingDocument".equals(value.Value)
								|| "com.sun.star.text.TextDocument".equals(value.Value)
								|| "com.sun.star.sheet.SpreadsheetDocument".equals(value.Value)
								|| "com.sun.star.text.WebDocument".equals(value.Value);
					}
				}
			}
		} catch (com.sun.star.uno.Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	public static PropertyValue[] setupLoadProps() {
		final PropertyValue[] propertyValues = new PropertyValue[4];
		propertyValues[0] = new PropertyValue();
		propertyValues[0].Name = "StatusIndicator";
		XStatusIndicator s = new XStatusIndicator() {
			public void start(final String s, final int i) {
				System.out.println("Core.start");
				System.out.println("s = " + s);
				System.out.println("i = " + i);
			}

			public void end() {
				System.out.println("Core.end");
			}

			public void setText(final String s) {
				System.out.println("Core.setText");
				System.out.println("s = " + s);
			}

			public void setValue(final int i) {
//					System.out.println("Core.setValue");
//					System.out.println("i = " + i);
			}

			public void reset() {
				System.out.println("Core.reset");
			}
		};
		propertyValues[0].Value = s;

		propertyValues[1] = new PropertyValue();
		propertyValues[1].Name = "MacroExecutionMode";
		propertyValues[1].Value = MacroExecMode.NEVER_EXECUTE;

		propertyValues[2] = new PropertyValue();
		propertyValues[2].Name = "UpdateDocMode";
		propertyValues[2].Value = UpdateDocMode.NO_UPDATE;

		propertyValues[3] = new PropertyValue();
		propertyValues[3].Name = "InteractionHandler";
		propertyValues[3].Value = new XInteractionHandler() {
			public void handle(final XInteractionRequest xInteractionRequest) {
				Object exObj = xInteractionRequest.getRequest();
				com.sun.star.uno.Exception ex = (com.sun.star.uno.Exception) UnoRuntime
						.queryInterface(com.sun.star.uno.Exception.class, exObj);
				System.out.println("msg" + ex.getMessage());
				System.out.println("toString()" + ex.toString());
				System.out.println("getLocalizedMessage()" + ex.getLocalizedMessage());
			}
		};
		return propertyValues;
	}
}
